import React, { useRef, useContext, useState, useEffect, useCallback } from 'react';
import { Row, Col, Spin, message, Empty, Button, Result, Tag } from 'antd';
import GGEditor, { Flow } from 'gg-editor';
import { useEventListener, useRequest, useThrottle, useThrottleFn } from 'ahooks';
import PubSub from 'pubsub-js';
import request from '@/utils/request';
import { getHeight } from '@/utils/utils';
import FlowContext from '../FlowContext';
import { FlowContextMenu } from '../components/EditorContextMenu';
import { FlowToolbar } from '../components/EditorToolbar';
import { FlowItemPanel } from '../components/EditorItemPanel';
import { FlowDetailPanel } from '../components/EditorDetailPanel';
import styles from './style.less';
import { FlowEditorProvider } from './FlowEditorContext';
import { TAB_CLOSE_CALLBACKS } from '@/layouts/BasicLayout';
import { getUserInfo, getUsername } from '@/utils/user';
import PageLoading from '@/components/PageLoading';

GGEditor.setTrackable(false);

// 扣掉编辑器`工具栏`的高度
const height = getHeight() - 45;

function FlowEditor() {
  const flowRef = useRef();

  const { state, dispatch } = useContext(FlowContext);

  const { processId, versionId, graph, readOnly, tabName } = state;

  const [saveLoading, setSaveLoading] = useState(false);

  const { loading: queryLoading, data = {}, run: refresh } = useRequest(
    () => request.post('/process/queryVersionData', { data: { processId, versionId, readOnly } }),
    {
      manual: true,
      onSuccess: response => {
        dispatch({ type: 'update_flow_data', payload: response });
      },
    }
  );

  useEffect(() => {
    if (processId && versionId && refresh) {
      // if (!readOnly) {
      //   TAB_CLOSE_CALLBACKS[
      //     `/flow/${processId}?versionId=${versionId}&tabName=${tabName}`
      //   ] = () => {
      //     console.log('releaseLock');
      //     navigator.sendBeacon(
      //       `v1/process/releaseLock?processId=${processId}&versionId=${versionId}`
      //     );
      //   };
      // }
      refresh();
    }
  }, [processId, refresh, versionId]);

  const handleSave = useCallback(
    (onlyGraph = false) => {
      if (flowRef.current && !readOnly) {
        const graph = flowRef.current.graph.save();
        const { nodes = [], edges = [] } = graph;

        if (!onlyGraph) {
          let endNodeCount = 0;
          let startNodeCount = 0;

          const nodeMap = {};

          if (nodes.length === 0) {
            message.info('流程图不能为空');
            return;
          }

          for (const node of nodes) {
            const { nodeType, nodeId, label, id } = node;
            nodeMap[id] = node;
            if (nodeType === 'Start' || nodeType === 'HttpDataSource') {
              startNodeCount++;
            }
            if (nodeType === 'End') {
              endNodeCount++;
            }
            // 1、存在未保存节点
            if (nodeType !== 'End' && nodeType !== 'Start' && !nodeId) {
              message.info(`【${label}】节点未保存`);
              return;
            }
          }

          // 2、结束节点只能有一个
          // if (endNodeCount === 0) {
          //   message.info(`缺少结束节点`);
          //   return;
          // }
          if (endNodeCount > 1) {
            message.info(`结束节点只能有一个`);
            return;
          }

          // 3、开始节点只能有一个
          // if (startNodeCount === 0) {
          //   message.info(`缺少开始节点`);
          //   return;
          // }
          if (startNodeCount > 1) {
            message.info(`开始节点只能有一个`);
            return;
          }

          // 4、只有开始节点和结束节点
          // if (nodes.length === 2) {
          //   message.info(`不能只有开始节点和结束节点`);
          //   return;
          // }

          // 5、非法连线数据
          for (const edge of edges) {
            if (!(typeof edge.source === 'string') || !(typeof edge.target === 'string')) {
              message.info(`连线的两端必须为节点`);
              return;
            }
            // 补充默认校验分支
            const { source, branch } = edge;
            if (source && nodeMap[source]) {
              const sourceNode = nodeMap[source];
              if (sourceNode.nodeType === 'Validation' && !branch) {
                edge.branch = 'VALIDATE_SUCCESS';
              }
              if (sourceNode.nodeType === 'HttpCall' && !branch) {
                edge.branch = 'CALL_SUCCESS';
              }
            }
          }
        }

        if (!onlyGraph) {
          setSaveLoading(true);
        }
        request
          .post('/process/saveVersionData', {
            data: { versionId, onlyGraph, graph: { nodes, edges } },
          })
          .then(() => {
            if (!onlyGraph) {
              refresh();
              message.success('保存成功');
            }
          })
          .finally(() => {
            if (!onlyGraph) {
              setSaveLoading(false);
            }
          });
      }
    },
    [refresh, versionId]
  );

  useEffect(() => {
    if (!readOnly) {
      PubSub.subscribe(`nodeSave_${state.processId}_${state.versionId}`, () => {
        handleSave(true);
      });
      PubSub.subscribe(`onNodeRemove_${state.processId}_${state.versionId}`, () => {
        handleSave(true);
      });
    }
    return () => {
      if (!readOnly) {
        PubSub.unsubscribe(`nodeSave_${state.processId}_${state.versionId}`);
        PubSub.unsubscribe(`onNodeRemove_${state.processId}_${state.versionId}`);
      }
    };
  }, [handleSave, readOnly, state.processId, state.versionId]);

  if (
    !queryLoading &&
    !saveLoading &&
    data.locked &&
    data.lockedBy !== getUsername() &&
    !readOnly
  ) {
    return (
      <Result
        status="403"
        title="流程被锁定"
        subTitle={
          <>
            抱歉，当前流程正在被 <span className="text-success">{data.lockedBy}</span>{' '}
            操作，请稍后再试~
          </>
        }
        extra={
          <Button type="primary" onClick={refresh}>
            刷新一下
          </Button>
        }
      />
    );
  }

  return (
    <Spin spinning={queryLoading || saveLoading}>
      <GGEditor className={styles.editor}>
        <FlowEditorProvider>
          <Row className={styles.toolbar}>
            <Col span={24}>
              <FlowToolbar handleSave={handleSave} />
            </Col>
          </Row>
          <div className={styles.main}>
            {readOnly ? null : (
              <div className={styles.left} style={{ height: `${height}px` }}>
                <FlowItemPanel data={data} />
              </div>
            )}
            <div className={styles.canvas}>
              <Flow
                style={{ height: `${height}px`, width: '100%' }}
                ref={ref => {
                  flowRef.current = ref;
                }}
                data={graph}
              />
            </div>
            <div className={styles.right}>
              <FlowDetailPanel />
            </div>
          </div>
          <FlowContextMenu />
        </FlowEditorProvider>
      </GGEditor>
    </Spin>
  );
}

export default FlowEditor;
